﻿using Ndns.Packet.Helpers;
using Ndns.Packet.Text;
using System;
using System.IO;
using System.Text;

namespace Ndns.Packet.ResourceRecords
{
    public sealed class DnsResourceRecordTXT : DnsResourceRecord
    {
        private static PunycodeEncoding Punycode => PunycodeEncoding.Default;

        /// <summary>
        /// 表示应答中的A记录。
        /// </summary>
        public string Content { get; set; }

        public override DnsRecordType Type { get => DnsRecordType.TXT; set => throw new NotSupportedException(); }

        /// <summary>
        /// 根据相关信息构造DnsResourceRecordCNAME实例。
        /// </summary>
        /// <param name="hostName">DNS应答的主机名信息。</param>
        /// <param name="t">DNS应答的类型。</param>
        /// <param name="c">DNS应答的类别。</param>
        /// <param name="ttl">DNS应答的超时时间。</param>
        /// <param name="content">DNS应答的数据。</param>
        public DnsResourceRecordTXT(DnsHostName hostName, DnsClass c, uint ttl, string content)
            : base(hostName, c, ttl)
        {
            Content = content ?? throw new ArgumentNullException(nameof(content));
        }

        /// <summary>
        /// 从包含数据包的流中以支持指针的方式构造实例。
        /// </summary>
        /// <param name="packetStream">数据包流，Position应该指向要解析的应答的开始。</param>
        /// <param name="offset">数据包的开始位置在流中的偏移量。</param>
        /// <returns>解析得到的实例。</returns>
        public static new DnsResourceRecordTXT FromPacket(Stream packetStream, long packetStartIndex)
        {
            var hostName = DnsHostName.FromPacket(packetStream, packetStartIndex);
            var vType = packetStream.ReadNetworkUInt16();
            var vClass = packetStream.ReadNetworkUInt16();
            var vTTL = packetStream.ReadNetworkUInt32();
            int len = packetStream.ReadNetworkUInt16();
            PacketEndOfStreamException.CheckValue(vType, vClass, vTTL, len);

            if ((DnsRecordType)vType != DnsRecordType.TXT)
                throw new FormatException();

            string vContent;
            using (var ms = new MemoryStream())
            {
                int bytesCopyed = packetStream.CopyBytesTo(ms, len, 256);
                if (bytesCopyed != len)
                    PacketEndOfStreamException.CheckValue(-1);

                ms.Seek(0, SeekOrigin.Begin);
                len = ms.ReadByte();
                byte bLen = (byte)len;
                byte[] buffer = new byte[bLen];
                ms.Read(buffer);
                vContent = Punycode.DecodeDomainPart(Encoding.ASCII.GetString(buffer));
            }
            return new DnsResourceRecordTXT(hostName, (DnsClass)vClass, (uint)vTTL, vContent);
        }

        public override DnsResourceRecord Clone()
        {
            return new DnsResourceRecordTXT(HostName, Class, TTL, Content);
        }

        public override byte[] GetData()
        {
            using (var ms = new MemoryStream())
            {
                ms.Seek(1, SeekOrigin.Begin);
                int count = Punycode.EncodeDomainPartToStream(Content, ms);
                ms.Seek(0, SeekOrigin.Begin);
                if (count <= Byte.MaxValue)
                {
                    ms.WriteByte((byte)count);
                    return ms.ToArray();
                }
                ms.WriteByte(Byte.MaxValue);
                byte[] buffer = new byte[Byte.MaxValue + 1];
                ms.Read(buffer);
                return buffer;
            }
            
        }
        
        protected override int GetDataHashCode()
        {
            return Content.GetHashCode();
        }

        protected override bool TestDataEqualsTo(DnsResourceRecord other)
        {
            var record = other as DnsResourceRecordTXT;
            if (record == null)
                return false;
            return Content == record.Content;
        }
        
    }
}
